package com.code.lightning;

import com.code.lightning.code.base.BasicDBConnect;
import com.code.lightning.common.DataBaseInfoAnalyseFactory;
import com.code.lightning.common.api.BasicDataBaseInfoAnalyse;
import com.code.lightning.common.api.BasicEntityConvert;
import com.code.lightning.common.convert.EntityConvertFactory;
import com.code.lightning.common.domain.BasicClassConvertMetaData;
import com.code.lightning.common.domain.BasicTableFieldMetaData;
import com.code.lightning.common.domain.BasicTableMetaData;
import com.code.lightning.common.exception.LightingGeneratorException;
import com.code.lightning.config.BasicGeneratorConfig;
import com.code.lightning.generator.LightingCodeTemplateGenerator;
import com.code.lightning.generator.TemplateKeys;
import com.code.lightning.generator.TemplateModel;
import com.code.lightning.utils.FileUtil;
import com.code.lightning.utils.PropertiesUtil;
import com.code.lightning.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;

import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

/**
 * 生成工具类
 * 可通过该类访问各个接口
 * @author LGX_TvT
 * @date 2021-01-20 0:40
 */
@Slf4j
public class GeneratorHelper {

    private static GeneratorHelper generatorHelper = new GeneratorHelper();

    /**
     * 配置
     */
    private BasicGeneratorConfig config;

    /**
     * Connection连接
     */
    private Connection connection;

    private GeneratorHelper() {}

    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    public void setBasicGeneratorConfig(BasicGeneratorConfig config) {
        this.config = config;
    }

    /**
     * 初始化自动生成工具类
     * @param config
     * @return
     */
    public static GeneratorHelper getInstance(BasicGeneratorConfig config) {
        BasicDBConnect.init(config);
        generatorHelper.setBasicGeneratorConfig(config);
        generatorHelper.setConnection(BasicDBConnect.getConnect());
        return generatorHelper;
    }

    public static GeneratorHelper getInstance() {
        BasicGeneratorConfig config = PropertiesUtil.getConfig();
        return getInstance(config);
    }

    /**
     * 关闭连接，释放内存
     */
    public void close() {
        BasicDBConnect.close(this.connection, null, null);
        this.connection = null;
    }

    /* ================== 模板生成 ================== */

    /**
     * 根据配置文件生成文件
     */
    public void generatorFile() throws LightingGeneratorException {
        check();
        EntityConvertFactory convertFactory = EntityConvertFactory.getInstance();
        BasicEntityConvert basicEntityConvert = convertFactory.createConvert(this.config.getLanguage());
        BasicDataBaseInfoAnalyse infoAnalyse = getBasicDataBaseInfoAnalyse();
        List<String> includeTables = this.config.getIncludeTables();
        List<String> excludeTables = this.config.getExcludeTables();
        List<BasicClassConvertMetaData> entityMetaData = new ArrayList<>();
        Map<String, Object> defaultModels = new TemplateKeys().getDefaultKeys(this.config);

        // 全选
        try {
            List<BasicTableMetaData> tableMetaData = infoAnalyse.getTableMetaData(this.connection.getCatalog());
            if (CollectionUtils.isNotEmpty(tableMetaData)) {
                for (BasicTableMetaData tableMetaDatum : tableMetaData) {
                    // 排除
                    if (excludeTables.contains(tableMetaDatum.getTableName())) {
                        continue;
                    }
                    if (includeTables.size() == 1 && "*".equals(includeTables.get(0))) {
                        List<BasicTableFieldMetaData> tableFieldMetaData = infoAnalyse.getTableFieldMetaData(this.connection.getCatalog(), tableMetaDatum.getTableName());
                        BasicClassConvertMetaData metaData = basicEntityConvert.convertEntity(tableMetaDatum, tableFieldMetaData, this.config);
                        if (metaData != null) {
                            entityMetaData.add(metaData);
                            log.info("Parse db [{}], table [{}] => [{}] success...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                        } else {
                            log.error("Parse db [{}], table [{}] => [{}] fail...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                        }
                    } else {
                        if (includeTables.contains(tableMetaDatum.getTableName())) {
                            List<BasicTableFieldMetaData> tableFieldMetaData = infoAnalyse.getTableFieldMetaData(this.connection.getCatalog(), tableMetaDatum.getTableName());
                            BasicClassConvertMetaData metaData = basicEntityConvert.convertEntity(tableMetaDatum, tableFieldMetaData, this.config);
                            if (metaData != null) {
                                entityMetaData.add(metaData);
                                log.info("Parse db [{}], table [{}] => [{}] success...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                            } else {
                                log.error("Parse db [{}], table [{}] => [{}] fail...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                            }
                        }
                    }
                }
            }
        } catch (SQLException throwables) {
            log.error("get TableMetaData Error: {}", throwables.getMessage());
            throwables.printStackTrace();
        }

        File templateInputPath = new File(this.config.getTemplateBaseInputPath());
        if (!templateInputPath.exists() || !templateInputPath.isDirectory()) {
            log.error("template InputPath can not find!");
            throw new LightingGeneratorException("template InputPath can not find!");
        }
        File[] templateFiles = templateInputPath.listFiles();

        // 模板映射
        Map<String, String> templateMapping = this.config.getTemplateMapping();
        Set<String> mappingKeys = templateMapping.keySet();
        if (ArrayUtils.isNotEmpty(templateFiles)) {
            for (File templateFile : templateFiles) {
                if (CollectionUtils.isNotEmpty(mappingKeys)) {
                    for (String mappingKey : mappingKeys) {
                        if (templateFile.getName().equals(templateMapping.get(mappingKey))) {
                            // 创建生成目标路径
                            String targetPath = StringUtil.packageName2Path(this.config.getTemplateBaseOutputPath() + "/" + this.config.getPackageName() + "/" + mappingKey);
                            new LightingCodeTemplateGenerator().fileTemplateGenerator(entityMetaData, defaultModels, templateFile.getAbsolutePath(), targetPath);
                        }
                    }
                }
            }
        }
    }

    /**
     * 根据配置文件生成字符串
     */
    public Map<String, List<LightingCodeTemplateGenerator.GeneratorEntity>> generatorString() throws LightingGeneratorException {
        check();
        Map<String, List<LightingCodeTemplateGenerator.GeneratorEntity>> stringListMap = new HashMap<>();
        EntityConvertFactory convertFactory = EntityConvertFactory.getInstance();
        BasicEntityConvert basicEntityConvert = convertFactory.createConvert(this.config.getLanguage());
        BasicDataBaseInfoAnalyse infoAnalyse = getBasicDataBaseInfoAnalyse();
        List<String> includeTables = this.config.getIncludeTables();
        List<String> excludeTables = this.config.getExcludeTables();
        List<BasicClassConvertMetaData> entityMetaData = new ArrayList<>();
        Map<String, Object> defaultModels = new TemplateKeys().getDefaultKeys(this.config);

        // 全选
        try {
            List<BasicTableMetaData> tableMetaData = infoAnalyse.getTableMetaData(this.connection.getCatalog());
            if (CollectionUtils.isNotEmpty(tableMetaData)) {
                for (BasicTableMetaData tableMetaDatum : tableMetaData) {
                    // 排除
                    if (excludeTables.contains(tableMetaDatum.getTableName())) {
                        continue;
                    }
                    if (includeTables.size() == 1 && "*".equals(includeTables.get(0))) {
                        List<BasicTableFieldMetaData> tableFieldMetaData = infoAnalyse.getTableFieldMetaData(this.connection.getCatalog(), tableMetaDatum.getTableName());
                        BasicClassConvertMetaData metaData = basicEntityConvert.convertEntity(tableMetaDatum, tableFieldMetaData, this.config);
                        if (metaData != null) {
                            entityMetaData.add(metaData);
                            log.info("Parse db [{}], table [{}] => [{}] success...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                        } else {
                            log.error("Parse db [{}], table [{}] => [{}] fail...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                        }
                    } else {
                        if (includeTables.contains(tableMetaDatum.getTableName())) {
                            List<BasicTableFieldMetaData> tableFieldMetaData = infoAnalyse.getTableFieldMetaData(this.connection.getCatalog(), tableMetaDatum.getTableName());
                            BasicClassConvertMetaData metaData = basicEntityConvert.convertEntity(tableMetaDatum, tableFieldMetaData, this.config);
                            if (metaData != null) {
                                entityMetaData.add(metaData);
                                log.info("Parse db [{}], table [{}] => [{}] success...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                            } else {
                                log.error("Parse db [{}], table [{}] => [{}] fail...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                            }
                        }
                    }
                }
            }
        } catch (SQLException throwables) {
            log.error("get TableMetaData Error: {}", throwables.getMessage());
            throwables.printStackTrace();
        }

        File templateInputPath = new File(this.config.getTemplateBaseInputPath());
        if (!templateInputPath.exists() || !templateInputPath.isDirectory()) {
            log.error("template InputPath can not find!");
            throw new LightingGeneratorException("template InputPath can not find!");
        }
        File[] templateFiles = templateInputPath.listFiles();

        // 模板映射
        Map<String, String> templateMapping = this.config.getTemplateMapping();
        Set<String> mappingKeys = templateMapping.keySet();
        if (ArrayUtils.isNotEmpty(templateFiles)) {
            for (File templateFile : templateFiles) {
                if (CollectionUtils.isNotEmpty(mappingKeys)) {
                    for (String mappingKey : mappingKeys) {
                        if (templateFile.getName().equals(templateMapping.get(mappingKey))) {
                            String template = FileUtil.readToString(templateFile);
                            String templateFullPath = templateMapping.get(mappingKey);
                            List<LightingCodeTemplateGenerator.GeneratorEntity> entities = new LightingCodeTemplateGenerator().stringTemplateGenerator(entityMetaData, defaultModels, template, templateFullPath);
                            stringListMap.put(mappingKey, entities);
                        }
                    }
                }
            }
        }
        return stringListMap;
    }

    /**
     * 根据配置文件生成字符串
     * @param templates 模板集合
     * @return Map<String, List<LightingCodeTemplateGenerator.GeneratorEntity>>
     * @throws LightingGeneratorException
     */
    public Map<String, List<LightingCodeTemplateGenerator.GeneratorEntity>> generatorString(List<TemplateModel> templates) throws LightingGeneratorException {
        check();
        Map<String, List<LightingCodeTemplateGenerator.GeneratorEntity>> stringListMap = new HashMap<>();
        EntityConvertFactory convertFactory = EntityConvertFactory.getInstance();
        BasicEntityConvert basicEntityConvert = convertFactory.createConvert(this.config.getLanguage());
        BasicDataBaseInfoAnalyse infoAnalyse = getBasicDataBaseInfoAnalyse();
        List<String> includeTables = this.config.getIncludeTables();
        List<String> excludeTables = this.config.getExcludeTables();
        List<BasicClassConvertMetaData> entityMetaData = new ArrayList<>();
        Map<String, Object> defaultModels = new TemplateKeys().getDefaultKeys(this.config);

        // 全选
        try {
            List<BasicTableMetaData> tableMetaData = infoAnalyse.getTableMetaData(this.connection.getCatalog());
            if (CollectionUtils.isNotEmpty(tableMetaData)) {
                for (BasicTableMetaData tableMetaDatum : tableMetaData) {
                    // 排除
                    if (excludeTables.contains(tableMetaDatum.getTableName())) {
                        continue;
                    }
                    if (includeTables.size() == 1 && "*".equals(includeTables.get(0))) {
                        List<BasicTableFieldMetaData> tableFieldMetaData = infoAnalyse.getTableFieldMetaData(this.connection.getCatalog(), tableMetaDatum.getTableName());
                        BasicClassConvertMetaData metaData = basicEntityConvert.convertEntity(tableMetaDatum, tableFieldMetaData, this.config);
                        if (metaData != null) {
                            entityMetaData.add(metaData);
                            log.info("Parse db [{}], table [{}] => [{}] success...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                        } else {
                            log.error("Parse db [{}], table [{}] => [{}] fail...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                        }
                    } else {
                        if (includeTables.contains(tableMetaDatum.getTableName())) {
                            List<BasicTableFieldMetaData> tableFieldMetaData = infoAnalyse.getTableFieldMetaData(this.connection.getCatalog(), tableMetaDatum.getTableName());
                            BasicClassConvertMetaData metaData = basicEntityConvert.convertEntity(tableMetaDatum, tableFieldMetaData, this.config);
                            if (metaData != null) {
                                entityMetaData.add(metaData);
                                log.info("Parse db [{}], table [{}] => [{}] success...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                            } else {
                                log.error("Parse db [{}], table [{}] => [{}] fail...", this.connection.getCatalog(), tableMetaDatum.getTableName(), metaData);
                            }
                        }
                    }
                }
            }
        } catch (SQLException throwables) {
            log.error("get TableMetaData Error: {}", throwables.getMessage());
            throwables.printStackTrace();
        }

        // 模板映射
        if (CollectionUtils.isNotEmpty(templates)) {
            for (TemplateModel templateModel : templates) {
                List<LightingCodeTemplateGenerator.GeneratorEntity> entities = new LightingCodeTemplateGenerator().stringTemplateGenerator(entityMetaData, defaultModels, templateModel.getTemplateContent(), templateModel.getTemplateName());
                stringListMap.put(templateModel.getTemplateMapping(), entities);
            }
        }
        return stringListMap;
    }

    /**
     * 数据检查
     */
    private void check() {
        if (this.config == null) {
            log.error("Config is null!");
            throw new RuntimeException("Config is null!");
        }
        if (this.connection == null) {
            log.error("Connection is null!");
            throw new RuntimeException("Connection is null!");
        }
    }


    /* ================== 元数据 ================== */

    /**
     * 获取数据库元数据解析工具
     */
    public BasicDataBaseInfoAnalyse getBasicDataBaseInfoAnalyse() throws LightingGeneratorException {
        DataBaseInfoAnalyseFactory factory = DataBaseInfoAnalyseFactory.getInstance();
        return factory.create(this.config.getDbCategory(), this.connection);
    }



}
